<?php

namespace common\model;

use support\ErrorCode;
use think\Model;
use think\module\Module;

class FieldModel extends Model
{

    //自动验证
    protected $_validate = [
        ['table', '', '', self::MUST_VALIDATE, 'require', self::MODEL_INSERT],//必须字段
        ['table', '1,36', '', self::EXISTS_VALIDATE, 'length'],
        ['config', '', '', self::EXISTS_VALIDATE, 'array']
    ];

    //自动完成
    protected $_auto = [
        ['config', 'json_encode', self::EXISTS_VALIDATE, 'function'],
        ['uuid', 'create_uuid', self::MODEL_INSERT, 'function'],
        ['description', 'fill_text_default_val', self::MODEL_INSERT, 'function']
    ];

    /**
     * 获取器：配置
     * @param $value
     * @return array|mixed
     */
    public function getConfigAttr($value)
    {
        if (!empty($value)) {
            return json_decode($value, true);
        }
        return [];
    }

    /**
     * 关联表配置
     * [
     *   "module" => 所属模块,
     *   "table" => 所属表名,
     *   "fields" => 字段名,
     *   "lang" => 字段语言包,
     *   "type" => 字段类型,
     *   "editor" => 编辑器类型（none, input, textarea, combobox, date, datetime, radio, url_link）,
     *   "edit" => 是否可以编辑（allow, deny）,
     *   "show" => 是否在前台显示 （yes, no）,
     *   "value_show" => 前台显示值key名称
     *   "filter" => 是否可以过滤（allow, deny）,
     *   "sort" => 是否可以排序（allow, deny）,
     *   "validate" => 验证方法,
     *   "mask" => input 输入掩码,
     *   "multiple" => combobox专属 是否可以多选,
     *   "group" => combobox专属 是否分组显示,
     *   "allow_group" => 是否为可以作为分组字段,
     *   "field_type" => 字段是内置字段还是自定义字段
     * ]
     * @param $moduleCode
     * @param $moduleData
     * @param array $field
     * @return array
     */
    protected function defaultFieldConfig($moduleCode, $moduleData, $field = ["name" => "test", "type" => "", "field_type" => "built_in"])
    {
        $tableModuleName = $moduleData[$moduleCode]['type'] === 'entity' ? 'Entity' : $moduleCode;
        return [
            "id" => 0, // 字段id, 固定字段是0，自定义字段是注册的id值
            "field" => $field["name"], // 字段名
            "lang" => string_initial_letter($field["name"], '_'), // 显示语言包key值
            "type" => $field["type"], // 字段类型，int、string、char
            "field_type" => !empty($field["field_type"]) ? $field["field_type"] : 'built_in', //字段类型 built_in：固定字段，custom：自定义字段
            "disabled" => "no", // 是否禁用（yes, no）
            "require" => "no", // 是否必须（yes, no）
            "table" => string_initial_letter($tableModuleName), //所属表名
            "module_code" => $moduleCode, // 模块编码
            "module_id" => $moduleData[$moduleCode]['id'], // 模块id
            "editor" => "none", // 编辑器类型
            "edit" => "deny", // 是否可以编辑（allow, deny）
            "show" => "no", // 是否在前台显示 （yes, no）
            "sort" => "deny", // 是否可以排序（allow, deny）
            "group" => "deny", // 是否可以分组
            "group_name" => "",  // 分组显示名称
            "filter" => "deny", // 是否可以过滤（allow, deny）
            "multiple" => "no",  // 是否可以多选（yes, no）
            "format" => [], // 格式化配置
            "validate" => "", // 验证方法
            "mask" => "", // 掩码配置
            "is_primary_key" => "no", // 是否是主键（yes, no）
            "is_foreign_key" => "no", // 是否是外键（yes, no）
            "placeholder" => "no", // 输入框占位文本 （yes, no）
            "show_word_limit" => "no", // 是否显示输入字数统计 （yes, no）
            "autocomplete" => "no", // 是否自动补全 （yes, no）
            "value_icon" => "", // 值图标
            "label_icon" => "", // 文本图标
            "label_width" => 0, // 文本宽度
            "value_width" => 0, //  值宽度
            "is_label" => "no",  //  是否显示文本 （yes, no）
            "default_value" => "", // 默认值
            "data_source" => [ // 数据源
                "type" => "fixed", // 数据源类型，fixed 固定 , dynamic 动态
                "data" => [] // 数据源，静态直接配置，动态是一个字符串标识
            ]
        ];
    }

    /**
     * 创建自定义字段
     * @param $param
     * @return mixed
     * @throws \think\Exception
     */
    public function createCustomField($param)
    {
        $param['config']['module_id'] = $param['module_id'];

        // 获取当前模块的code
        $moduleModel = \model(ModuleModel::class);
        $moduleItem = $moduleModel->field('id,code,type')->where(['id' => $param['module_id']])->find();

        // 获取模块映射数组
        $moduleData = $this->getModuleCodeMap();
        $tableModuleName = $moduleItem['type'] === 'entity' ? 'Entity' : $moduleItem['code'];

        $param['table'] = strtolower($tableModuleName);
        $param['type'] = "custom";


        $standardFieldConfig = $this->defaultFieldConfig($moduleItem['code'], $moduleData);
        $standardFieldConfig['field_type'] = "custom";

        foreach ($standardFieldConfig as $standardKey => $standardVal) {
            if ($standardKey !== "id" && !array_key_exists($standardKey, $param['config'])) {
                $param['config'][$standardKey] = $standardVal;
            }
        }

        if ($this->create($param, self::MODEL_INSERT)) {
            $result = $this->add();
            if (!$result) {
                //新增失败
                throw_strack_exception($this->getError(), ErrorCode::CREATE_CUSTOM_FIELD_FAILURE);
            } else {
                //新增成功，返回当前添加的一条完整数据
                $param['config']['id'] = $result;
                $param['id'] = $result;
                return $param;
            }
        } else {
            //数据验证失败，返回错误
            throw_strack_exception($this->getError(), ErrorCode::CREATE_CUSTOM_FIELD_PARAM);
        }
    }

    /**
     * 获取模块编码映射数据
     * @return array
     */
    public function getModuleCodeMap()
    {
        $moduleModel = \model(ModuleModel::class);
        $moduleData = $moduleModel->field("id,name,code,type")->select();
        return array_column($moduleData, null, 'code');
    }

    /**
     * 获取数据库的所有表
     * @param string $dbName
     * @return array
     */
    public function getTables($dbName = '')
    {
        return $this->db()->getTables($dbName);
    }

    /**
     * 取得数据表的字段信息
     * @param $tableName
     * @return mixed
     */
    public function getFields($tableName)
    {
        return $this->db()->getFields($tableName);
    }

    /**
     * 获取所有数据表字段(去除重复)
     * @return array
     */
    public function getAllTablesFields()
    {
        $tables = $this->getTables();
        $fields = [];
        foreach ($tables as $tableName) {
            $tableFields = $this->getFields($tableName);
            foreach ($tableFields as $field) {
                if (!in_array($field['name'], $fields)) {
                    $fields[] = $field['name'];
                }
            }
        }
        return $fields;
    }

    /**
     * 判断当前表是否存在指定字段
     * @param $tableName
     * @param $field
     * @return bool
     */
    public function checkTableField($tableName, $field)
    {
        $fields = $this->getFields($tableName);
        if (array_key_exists($field, $fields)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取模块真实的表名
     * @param $moduleCode
     * @return string
     */
    protected function getModuleRealTableName($moduleCode)
    {
        if (!array_key_exists($moduleCode, Module::$moduleDictData['module_index_by_code'])) {
            throw_strack_exception('The current module does not exist.', ErrorCode::MODULE_DOES_NOT_EXIST);
        }

        if (Module::$moduleDictData['module_index_by_code'][$moduleCode]['type'] === 'entity') {
            return 'entity';
        }

        return $moduleCode;
    }

    /**
     * 保存固定字段配置
     * @param $tableName
     * @param $fixedFieldConfig
     * @throws \think\Exception
     */
    protected function saveFixedFieldConfig($tableName, $fixedFieldConfig)
    {
        $count = $this->where(["table" => $tableName, 'type' => 'built_in'])->count();

        if ($count > 0) {
            $tables = $this->where(['field.table' => $tableName, 'field.type' => 'built_in'])->join("module on module.id = field.module_id", "LEFT")->field("field.id as field_id,module.id as module_id,module.code as module_code")->select();
            foreach ($tables as $table) {
                $fixedFieldConfig = array_map(function ($item) use ($table) {
                    $item['module_id'] = $table['module_id'];
                    $item['module_code'] = $table['module_code'];
                    return $item;
                }, $fixedFieldConfig);
                $this->where(["id" => $table['field_id']])->setField("config", json_encode($fixedFieldConfig));
            }
        } else {
            // 新增固定字段的时候 追加module信息
            $addData = [
                "table" => $tableName,
                'type' => 'built_in',
                'module_id' => Module::$moduleDictData['module_index_by_code'][$tableName]['id'],
                'uuid' => create_uuid(),
                "config" => json_encode($fixedFieldConfig)
            ];
            $this->add($addData);
        }
    }

    /**
     * 保存自定义字段配置
     * @param $tableName
     * @param $customFieldConfig
     */
    protected function saveCustomFieldConfig($tableName, $customFieldConfig)
    {
        $filter = [
            'type' => 'custom',
            'table' => $tableName
        ];
        foreach ($customFieldConfig as $customFieldItem) {
            $filter['_string'] = "json_extract(config,'$.field') = '{$customFieldItem['field']}'";
            if ($this->where($filter)->count()) {
                $this->where($filter)->setField("config", json_encode($customFieldItem));
            }
        }
    }

    /**
     * 保存当前字段配置修改
     * @param $tableName
     * @param $config
     * @return array
     * @throws \Exception
     */
    public function modifyFieldConfig($tableName, $config)
    {
        $model = new Model();
        $realTableName = $this->getModuleRealTableName($tableName);
        $model->startTrans();//强制开启事务
        try {
            if (!empty($config['fixed'])) {
                // 处理固定字段
                $this->saveFixedFieldConfig($realTableName, $config['fixed']);
            }

            if (!empty($config['custom'])) {
                // 处理自定义字段
                $this->saveCustomFieldConfig($realTableName, $config['custom']);
            }
            $model->commit();
        } catch (\Exception $e) {
            $model->rollback();
            throw_strack_exception($e->getMessage(), ErrorCode::MODIFY_FIELD_CONFIG_FAILURE);
        }

        return [];
    }

    /**
     * 获取指定表字段控件设置
     * @param $tableName
     * @return array
     */
    public function getTableFields($tableName)
    {
        // 判断当前模块是不是自定义模块

        $moduleModel = \model(ModuleModel::class);
        $tableModuleData = $moduleModel->field('id,type')->where(['code' => $tableName])->find();

        $tableRealName = $tableModuleData['type'] === 'entity' ? 'entity' : $tableName;
        $fixedFilter = ["module_id" => $tableModuleData['id'], "type" => "built_in"];

        $count = $this->where($fixedFilter)->count();

        //获取当前表存在字段
        $fields = $this->db->getFields($tableRealName);

        // 获取模块映射数组
        $moduleData = $this->getModuleCodeMap();

        $config = [];
        if ($count > 0) {
            $configJson = $this->where($fixedFilter)->getField("config");

            $existFieldConfig = json_decode($configJson, true);

            $existFieldIndex = [];
            foreach ($existFieldConfig as $existItem) {
                $existFieldIndex[$existItem["field"]] = $existItem;
            }
            //判断当前存储字段是否在数据表中，不存在忽略，数据库表中新增字段增加进来
            foreach ($fields as $key => $field) {
                if (array_key_exists($key, $existFieldIndex)) {
                    array_push($config, $existFieldIndex[$key]);
                } else {
                    array_push($config, $this->defaultFieldConfig($tableName, $moduleData, $field));
                }
            }
        } else {
            foreach ($fields as $key => $field) {
                array_push($config, $this->defaultFieldConfig($tableName, $moduleData, $field));
            }
        }

        // 填充自定义字段
        $customFields = $this->field("id,config")->where(["module_id" => $tableModuleData['id'], "type" => "custom"])->select();
        if (!empty($customFields)) {
            foreach ($customFields as $customField) {
                $customFieldConfig = json_decode($customField['config'], true);
                $customFieldConfig['id'] = $customField['id'];
                $config[] = $customFieldConfig;
            }
        }

        return $config;
    }

    /**
     * 获取所有模块字段按照模块id映射数据
     * @param array $moduleMapData
     * @return array
     */
    public function getAllModuleFieldsMapData($moduleMapData = [])
    {
        $allFieldsData = $this->field('type,module_id,config')->select();

        // 已经 module_id 为键值，按固定和自定义分组划分字段数据
        $fieldModuleMapData = [];
        foreach ($allFieldsData as $allFieldsItem) {

            if (!empty($moduleMapData) && isset($moduleMapData[$allFieldsItem['module_id']])) {
                $moduleKey = $moduleMapData[$allFieldsItem['module_id']]["code"];
            } else {
                $moduleKey = $allFieldsItem['module_id'];
            }

            if (!array_key_exists($moduleKey, $fieldModuleMapData)) {
                $fieldModuleMapData[$moduleKey] = [
                    'fixed' => [],
                    'custom' => []
                ];
            }

            $config = json_decode($allFieldsItem['config'], true);
            if ($allFieldsItem['type'] === 'built_in') {
                // 固定字段
                foreach ($config as $item) {
                    $fieldModuleMapData[$moduleKey]['fixed'][$item['field']] = $item;
                }
            } else {
                // 自定义字段
                $fieldModuleMapData[$moduleKey]['custom'][$config['field']] = $config;
            }
        }

        return $fieldModuleMapData;
    }

    /**
     * 获取当前表的字段配置
     * @param string $tableName
     * @param array $moduleData
     * @return mixed
     */
    public function getTableFieldsConfig($tableName = '', $moduleData = [])
    {
        $tableModuleName = str_replace(C("DB_PREFIX"), "", $tableName);
        $configJson = $this->where(["table" => $tableModuleName])->getField("config");
        $fieldConfig = json_decode($configJson, true);
        // 给内置字段加上module_code标识
        foreach ($fieldConfig as &$fieldsItem) {
            if (!empty($moduleData)) {
                $fieldsItem["module_code"] = $moduleData["code"];
                $fieldsItem["module_type"] = $moduleData["type"];
                // 判断是不是外联字段
                if (array_key_exists("is_foreign_key", $fieldsItem) && $fieldsItem["is_foreign_key"] === "yes") {
                    $fieldsItem["foreign_key"] = $fieldsItem["fields"];
                    $fieldsItem["frozen_module"] = $fieldsItem["module_code"];
                }
            }
        }
        return $fieldConfig;
    }
}
